#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string>
#include <time.h>
#include "../hdr/ArbolContextosPPMC.h"
#include "../hdr/Vector.h"

using namespace std;

#define MAXBUF 32768
#define MAXORDEN 8
#define MAXORDEN_FIJO 5
#define COMPRIME 1

int es_alfabetico(unsigned char c) {
	if (c >= 'A' && c <= 'Z')
		return 1;
	if (c >= 'a' && c <= 'z')
		return 1;
	return 0;
}

/* Se supone que puedo abrir cualquier archivo, mas alla del
 * tamanio, pero no lo puedo levantar completo en memoria.
 * Debido a esto, voy tomando bloques de 32 Kb.
 */

/* Hay cuatro combinaciones a tratar:
 * a) primer bloque, primeros MAXORDEN caracteres
 * b) primer bloque, resto de los caracteres
 * c) resto de los bloques, primeros MAXORDEN caracteres
 * d) resto de los bloques, resto de los caracteres.
 * Esto me genera el asco de los if's anidados.
 */

int comprime(char* archivo) {

	FILE* f = fopen(archivo, "rb");
	string s = string(archivo);
	s += ".ppm";
	FILE* comprimido = fopen(s.c_str(), "wb");
	if ((!f) || (!comprimido))
		exit(1);

	ArbolContextosPPMC arbol(comprimido, COMPRIME);
	unsigned char buffer[MAXBUF + MAXORDEN + 1];
	size_t leido, i, total = 0, orden = 0, contador_alfabetico = 0;

	leido = fread(buffer, sizeof(char), MAXORDEN, f);
	buffer[leido] = 0;
//	printf("Cadena: (%s)\n", buffer);
	for (i = 0; i < leido; i++) {
//		unsigned char ctx[16];
//		strncpy((char*) ctx, (char*) buffer + i - orden, orden + 1);
//		ctx[orden] = 0;
//		printf("Ctx: %s (orden %d) contador %d\n", ctx, orden, contador_alfabetico);
		arbol.procesarCaracter(buffer[i], (unsigned char*) buffer + i - orden,
				orden);
		contador_alfabetico +=
				(es_alfabetico(buffer[i])) ? 1 : (-contador_alfabetico);
		orden = (i < MAXORDEN_FIJO) ? i : MAXORDEN_FIJO;
		if (orden < contador_alfabetico)
			orden = (contador_alfabetico < MAXORDEN) ?
					contador_alfabetico : MAXORDEN;
		total++;
	}

	leido = fread(buffer + MAXORDEN, sizeof(char), MAXBUF, f);
	while (leido > 0) {
//		buffer[leido + MAXORDEN] = 0;
//		printf("Cadena: (%s)\n", buffer + MAXORDEN);

		for (i = 0; i < leido; i++) {
//			unsigned char ctx[16];
//			strncpy((char*) ctx, (char*) buffer + i + MAXORDEN - orden,
//					orden + 1);
//			ctx[orden] = 0;
//			printf("Ctx: %s (orden %d) contador %d\n", ctx, orden,
//					contador_alfabetico);
			arbol.procesarCaracter(buffer[i + MAXORDEN],
					(unsigned char*) (buffer + i + MAXORDEN - orden), orden);
			contador_alfabetico +=
					es_alfabetico(buffer[i + MAXORDEN]) ?
							1 : (-contador_alfabetico);
			orden = MAXORDEN_FIJO;
			if (orden < contador_alfabetico)
				orden = (contador_alfabetico < MAXORDEN) ?
						contador_alfabetico : MAXORDEN;
			total++;
			if (total % 524288 == 0)
				printf("%d kB comprimidos, %d nodos\n", total,
						NodoContexto::getCantidadNodos());
		}
		memcpy(buffer, buffer + leido, MAXORDEN);
		leido = fread(buffer + MAXORDEN, sizeof(char), MAXBUF, f);
	}

	arbol.procesarCaracter(256,
			(unsigned char*) (buffer + leido + MAXORDEN - orden), orden);
	arbol.terminarEmision();
	fclose(f);
	fclose(comprimido);
	return 0;
}

int descomprime(char* archivo) {
	FILE* comprimido = fopen(archivo, "r");
	string s = string(archivo);
	//s.erase(s.size() - 4, 4); // Borra el '.ppm' del nombre del archivo
	s += ".out";
	FILE* output = fopen(s.c_str(), "wb");
	if ((!comprimido) || (!output)) {
		fputs("Error de archivo", stderr);
		exit(1);
	}

	ArbolContextosPPMC arbol(comprimido, !COMPRIME);
	unsigned char caracter;
	unsigned char buffer[MAXBUF + MAXORDEN + 1];
	int resultado = 0, total = 0, orden = 0, contador_alfabetico = 0;

	// Ciclo de inicializacion
	for (int i = 0; i < MAXORDEN; i++) {
//		unsigned char ctx[16];
//		strncpy((char*) ctx, (char*) buffer + i - orden, orden + 1);
//		ctx[orden] = 0;
//		printf("Ctx: %s\n", ctx);
		resultado = arbol.procesarProbabilidad(&caracter, buffer + i - orden,
				orden);
		if (resultado == 1)
			break;
		fwrite(&caracter, sizeof(char), 1, output);
		buffer[i] = caracter;
		contador_alfabetico +=
				es_alfabetico(buffer[i]) ? 1 : (-contador_alfabetico);
		orden = (i < MAXORDEN_FIJO) ? i : MAXORDEN_FIJO;
		if (orden < contador_alfabetico)
			orden = (contador_alfabetico < MAXORDEN) ?
					contador_alfabetico : MAXORDEN;
		total++;
	}

	while (resultado == 0) {
//		buffer[leido + MAXORDEN] = 0;
//		printf("Cadena: (%s)\n", buffer + MAXORDEN);

		for (int i = 0; i < MAXBUF; i++) {
//			unsigned char ctx[16];
//			strncpy((char*) ctx, (char*) buffer + i + MAXORDEN - orden,
//					orden + 1);
//			ctx[orden] = 0;
//			printf("Ctx: %s\n", ctx);
			resultado = arbol.procesarProbabilidad(&caracter,
					buffer + i + MAXORDEN - orden, orden);
			if (resultado == 1)
				break;
			fwrite(&caracter, sizeof(char), 1, output);
			buffer[i + MAXORDEN] = caracter;
			contador_alfabetico +=
					es_alfabetico(buffer[i + MAXORDEN]) ?
							1 : (-contador_alfabetico);
			orden = MAXORDEN_FIJO;
			if (orden < contador_alfabetico)
				orden = (contador_alfabetico < MAXORDEN) ?
						contador_alfabetico : MAXORDEN;
			total++;
			if (total % 524288 == 0)
				printf("%d kB descomprimidos, %d nodos\n", total,
						NodoContexto::getCantidadNodos());
		}
		memcpy(buffer, buffer + MAXBUF, MAXORDEN);
	}

	fclose(comprimido);
	fclose(output);
	return 0;
}

void imprime_uso() {
	printf(
			"Funcionamiento por línea de comandos:\n\nTPGrupo8 -c pepe.txt\n"
					"Comprime el achivo pepe.txt generando el archivo pepe.txt.XX\n\n"
					"TPGrupo8 -x pepe.txt.XX\n"
					"Descomprime el archivo pepe.XX re-creando el archivo original (pepe.txt)\n");
}

int main(int argc, char* argv[]) {
	if (argc < 3) {
		imprime_uso();
		exit(1);
	}
	// Opciones cortas válidas: c comprime, x descomprime
	const char* const op_cortas = "c:x:";
	int opc = getopt_long(argc, argv, op_cortas, NULL, NULL);
	time_t ini, fin;
	time(&ini);
	if (opc == 'c')
		comprime(optarg);
	else if (opc == 'x')
		descomprime(optarg);

//	string s = "ww.txt";
//	comprime((char*) s.c_str());
//	s += ".ppm";
//	descomprime((char*) s.c_str());
	time(&fin);
	printf("Tiempo total: %.2lf\n", difftime(fin, ini));

	return 0;
}
